﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Workflows.Components.Entities.Distances.VectorDistances;

namespace Workflows.Components.Entities.Documents.Distances
{
    public class WeightedTermSimilarityCalculator : IFeatureVectorDistanceCalculator
    {
        #region IAttributeComparer Members
        //public string AssemblyFileName
        //{
        //    get { return this.GetType().Assembly.GetName().Name; }
        //    set { }
        //}

        //public string TypeFullName
        //{
        //    get { return this.GetType().FullName; }
        //    set { }
        //}

        public double CalculateDistance(object attr1, object attr2)
        {
            if (attr1.GetType() != typeof(BagOfWords) || attr2.GetType() != typeof(BagOfWords))
                throw new Exception("The attribute must be of type BagOfWords");

            BagOfWords docTermFreqs1 = (BagOfWords)attr1;
            BagOfWords docTermFreqs2 = (BagOfWords)attr2;
            if (docTermFreqs1 == null || docTermFreqs2 == null ||
                docTermFreqs1.TermFreqs == null || docTermFreqs2.TermFreqs == null ||
                docTermFreqs1.TermFreqs.Count == 0 || docTermFreqs2.TermFreqs.Count == 0)
                return double.PositiveInfinity;

            double score = 0;
            double perfectScore = 0;
            Dictionary<int, double> commonTerms = new Dictionary<int, double>();
            foreach (int termID in docTermFreqs1.TermFreqs.Keys)
            {
                double termScore = 0;
                if (docTermFreqs2.TermFreqs.ContainsKey(termID))
                {
                    termScore =
                        (docTermFreqs1.TermWeights[termID] * docTermFreqs1.TermFreqs[termID] +
                        docTermFreqs2.TermWeights[termID] * docTermFreqs2.TermFreqs[termID]);

                    commonTerms.Add(
                        termID,
                        Math.Min(docTermFreqs1.TermFreqs[termID], docTermFreqs2.TermFreqs[termID]));
                }
                score += termScore;
                double perfectTermScore = docTermFreqs1.TermWeights[termID] * docTermFreqs1.TermFreqs[termID];
                perfectScore += perfectTermScore;
            }
            foreach (int termID in docTermFreqs2.TermFreqs.Keys)
            {
                double perfectTermScore = docTermFreqs2.TermWeights[termID] * docTermFreqs2.TermFreqs[termID];
                if (docTermFreqs1.TermFreqs.ContainsKey(termID))
                {
                    // do nothing, already added
                }
                perfectScore += perfectTermScore;
            }

            return (double)(perfectScore - score) / perfectScore;
        }

        #endregion
    }
}
